<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>tractor game</title>
<style type="text/css">
  * {
    margin: 0;
    padding: 0;
  }
  html, body {
    width: 100%;
    height: 100%;
  }
</style>
<script type="text/javascript">
  var assets = (function() {
    var assets = {
      suitTexts: ['方块', '梅花', '红桃', '黑桃', '小王', '大王'],
      cardImages: [],
      numberTexts: '23456789十JQKA ',
    };
    for (var i = 0; i < 54; i++) {
      var img = new Image();
      img.src = 'images/' + i + '.BMP';
      assets.cardImages[i] = img;
    }
    assets.desktop = new Image();
    assets.desktop.src = 'images/desktop.BMP';
    return assets;
  })(), 
//------------------------------------------------------------------
  Card = (function() {
    // 私有变量
    var oneDeckOfCards = [],
    // 类
    cardClass = {
      // 枚举
      SUIT_SPADE: 3,
      SUIT_HEART: 2,
      SUIT_CLUB: 1,
      SUIT_DIAMOND: 0,
      SUIT_SMALL_JOKER: 4,
      SUIT_BIG_JOKER: 5,
      SUIT_TRUMP: 4,
      // SUIT_BEGIN_CYCLE: this.SUIT_DIAMOND,
      // SUIT_END_CYCLE: this.SUIT_SPADE,
      Count_Of_One_Suit: 13,
      NUMBER_2: 0,
      NUMBER_5: 3,
      NUMBER_10: 8,
      NUMBER_K: 11,
      INVALID: NaN,
      Joker_Xiao_52: 52,
      Joker_Da_53: 53,

      playedCards: [],
      
      sortCards: function(cards) {
        cards.sort(function(a, b) {
          return b.displayOrder - a.displayOrder;
        });
      },
      suitedCards: function(cards) {
        var suits = [[], [], [], [], []];
        for (var i = 0; i < cards.length; i++) {
          var card = cards[i];
          suits[card.trumpSuit()].push(card);
        }
        return suits;
      },
      frequency: function(cards, card) {
        var firstIndex = cards.indexOf(card);
        if (firstIndex == -1) {
          return 0;
        }
        var lastIndex = cards.lastIndexOf(card);
        return lastIndex - firstIndex + 1;
      },
      getCardBySuitAndNumber: function(suit, number) {
        if (suit >= 4) {
          return oneDeckOfCards[this.Joker_Xiao_52 + suit - this.SUIT_SMALL_JOKER];
        } else {
          return oneDeckOfCards[suit * 13 + number];
        }
      },
      getCardBySuitAndWeight: function(suit, weight) {
        if (suit >= 4) {
          return oneDeckOfCards[this.Joker_Xiao_52 + suit - this.SUIT_SMALL_JOKER];
        } else if (weight >= 12) {
          return this.getCardBySuitAndNumber(suit, game.masterNow());
        } else {
          var number = weight;
          if (weight >= game.masterNow()) {
            number++;
          }
          return this.getCardBySuitAndNumber(suit, number);
        }
      },
      getShuffledCardsForDealing: function() {
        // 拼接数组形成两副牌
        var cards = oneDeckOfCards.concat(oneDeckOfCards);
        // 洗牌，108次随机交换位置
        for (var i = 0; i < 108; i++) {
          var j = Math.floor(Math.random() * 108),
            tmp = cards[j];
          cards[j] = cards[i];
          cards[i] = tmp;
        }
        return cards;
      },
      /*
           大王 小王  主   副主  主牌  副牌
      display  53   52  51  50~48  47~36  35~24/23~12/11~0
      height   15   14  13  12  11~0   11~0
      */
      calculateOrders: function(flippedSuit, masterNumber) {
        // TODO:大小王的displayOrder可以不重复赋值
        oneDeckOfCards[Card.Joker_Da_53].displayOrder = 53;
        oneDeckOfCards[Card.Joker_Xiao_52].displayOrder = 52;
        oneDeckOfCards[Card.Joker_Da_53].weight
            = (isNaN(flippedSuit) || flippedSuit >= Card.SUIT_SMALL_JOKER) ? 14 : 15;
        oneDeckOfCards[Card.Joker_Xiao_52].weight = oneDeckOfCards[Card.Joker_Da_53].weight - 1;
        for (var suit = 0; suit < 4; suit++) {
          for (var number = 0; number < 13; number++) {
            var card = Card.getCardBySuitAndNumber(suit, number);
            if (number == masterNumber) {
              // 主
              if (suit == flippedSuit) {
                // 主牌主
                card.displayOrder = 51;
                card.weight = 13;
              } else {
                // 副主的最小order是48，如果亮主suit比suit大，则靠后1位
                // 注意，任何数跟NaN比大小都得到false
                var suitFallback = isNaN(flippedSuit) ? 0 : (suit > flippedSuit ? -1 : 0);
                card.displayOrder = 48 + suit + suitFallback;
                card.weight = 12;
              }
            } else {
              // 如果牌比主的数值大，则靠后1位
              // 注意，任何数比NaN比大小都得到false，故比较的顺序不能调换
              var fallback = number > masterNumber ? -1 : 0;
              if (suit == flippedSuit) {
                // 主牌
                card.displayOrder = 36 + number + fallback;
              } else {
                // 副牌
                var suitFallback = isNaN(flippedSuit) ? 0 : (suit > flippedSuit ? -1 : 0);
                card.displayOrder = (suit + suitFallback) * 12 + number + fallback;
              }
              card.weight = number + fallback;
            }
          } // for (var number = 0; number < 13; number++)
        } // for (var suit = 0; suit < 4; suit++)
      }, // calculateOrders
      getMarks: function(cards) {
        if (cards.constructor != Array.prototype.constructor) alert("getMarks should pass Array in");
        var marks = 0;
        for (var i = 0; i < cards.length; i++) {
          marks += cards[i].mark;
        }
        if (marks % 5 != 0) alert('getMarks % 5 != 0');
        return marks;
      },
      sameSuit: function(cards) {
        var sameSuit = true;
        for (var i = cards.length - 1; i >= 1; i--) {
          sameSuit &= cards[i].trumpSuit() == cards[0].trumpSuit();
        };
        return sameSuit;
      },
      // 对象 ------------------------
      createNew: function(index) {
        var card = {
            index: index,
            suit: Math.floor(index / this.Count_Of_One_Suit),
            number: Math.floor(index % this.Count_Of_One_Suit),
            mark: 0,
            displayOrder: 0,
            weight: 0
        };
        if (index >= 52) {
          card.suit = this.SUIT_SMALL_JOKER + index % 52;
          card.number = 13;
        }
        if (card.number == this.NUMBER_10 || card.number == this.NUMBER_K)
          card.mark = 10;
        else if (card.number == this.NUMBER_5)
          card.mark = 5;

        card.debug = assets.suitTexts[card.suit] + assets.numberTexts[card.number];

        card.isSameTo = function(card) {
            return this.index == card.index;
        };
        card.isMaster = function() {
            return this.number == game.masterNow();
        };
        card.isJoker = function() {
            return this.suit >= Card.SUIT_SMALL_JOKER;
        };
        card.isNormalTrump = function() {
            return this.suit == game.flippedSuit() && this.weight < 12;
        };
        card.isConstTrump = function() {
            return this.weight >= 12;
        };
        card.isTrump = function() {
          return this.suit == game.flippedSuit() || this.weight >= 12;
        };
        card.trumpSuit = function() {
          return this.isTrump() ? Card.SUIT_TRUMP : this.suit;
        };
        card.isLargerThan = function(that) {
          if (this.trumpSuit() == that.trumpSuit())
            return this.weight - that.weight >= 0;
          else
            // 主牌的那方大。
            return this.isTrump();
        };
        return card;
      },
    };
    // 静态变量初始化
    for (var i = 0; i < 54; i++) {
      oneDeckOfCards[i] = cardClass.createNew(i);
    }
    return cardClass;
  })(), // Card
//---------------------------------------------------------------
  CardPattern = {
    createNew: function(cards) {
      if (!Card.sameSuit(cards)) alert('not same suit');
      var pattern = {
          cards: cards,   // 传入的牌组
          singles: [],    // 单牌
          pairs: [],      // 对牌，二维数组，第二维是含两张牌的数组
          tractors: [],   // 拖拉机，二维数组，第二维是含4张牌或以上的数组

          calculate: function() {
            this.singles = [];
            this.pairs = [];
            this.tractors = [];
            if (this.cards.length == 1) {
              this.singles.push(this.cards[0]);
              return;
            }
            // 先找出对子
            var cards = this.cards.concat();
            while (cards.length > 1) {
              if (cards[0].isSameTo(cards[1])) {
                this.pairs.push([cards.shift(), cards.shift()]);
              } else {
                this.singles.push(cards.shift());
              }
            }
            if (cards.length == 1) {
              this.singles.push(cards[0]);
            } 
            // 再找出拖拉机
            var pairs = this.pairs.concat();
            while (pairs.length > 1) {
              if (pairs[0][0].weight - pairs[1][0].weight > 1) {
                pairs.shift();
                continue;
              }
              // 过滤相同的副主对
              while (pairs[0][0].weight == pairs[1][0].weight) {
                pairs.splice(1, 1);
              }
              if (pairs.length > 1
                && pairs[0][0].weight - pairs[1][0].weight == 1) {
                var tractor = pairs.shift().concat(pairs.shift()),
                  lastWeight = tractor[1][0].weight;
                while (pairs.length) {
                  if (lastWeight - pairs[0][0].weight > 1) {
                    break;
                  } else if (lastWeight == pairs[0][0].weight) {
                    pairs.shift();
                  } else {    // lastWeight - pairs[0][0].weight =  1
                    lastWeight--;
                    tractor = tractor.concat(pairs.shift());
                  }
                }
                this.tractors.push(tractor);
              }
            } // while (pairs.length > 1)
            // 冒泡排序，牌数从多到少排序拖拉机
            for (var i = 0; i < this.tractors.length; i++) {
              for (var j = i + 1; j < this.tractors.length; j++) {
                var ti = this.tractors[i],
                  tj = this.tractors[j];
                if (tj.length > ti.length) {
                  this.tractors[i] = tj;
                  this.tractors[j] = ti;
                }
              }
            }
          },  // calculate
          isThrow: function() {
            // 甩牌的意思就是单牌、对牌、拖拉机的多组合，所以这么算：
            var count = this.singles.length + this.pairs.length + this.tractors.length;
            return count != 1;
          },
          // 对子数，会把拖拉机也看做对子
          pairCount: function() {
            var count = this.pairs.length;
            for (var i = this.tractors.length - 1; i >= 0; i--) {
              count += this.tractors[i].length / 2;
            };
            return count;
          },
          // this和that是否牌型相同，要注意调用关系，判断是以this为基准的，
          // 而且必须有基础，比如对牌可以看成两个单牌
          samePattern: function(that) {
            // 循环this的拖拉机，不断用that牌组来满足this的牌型
            // 把满足的牌从that中移除
            // 如果
            for (var i = 0; i < this.tractors.length; i++) {
              var tractor = this.tractors[i];
              if (that.tractors.length == 0
                || that.tractors[0].length < tractor.length) {
                return false;
              } else {
                var cards = that.cards.concat();
                for (var i = that.tractors[0].length - 1; i >= 0; i--) {
                  cards.splice(cards.indexOf(that.tractors[0][i]), 1);
                };
                that = CardPattern.createNew(cards);
              }
            }
            // 如果剩余的对牌数不相等即牌型不等。单牌数是必然相等的，不再比较
            return this.pairs.length == that.pairCount();
          },
          canKeepLargestOnCards: function(cards) {
            // 如果that不是同花色，this肯定大
            if (!Card.sameSuit(cards)) {
              return true;
            }
            var that = Pattern.createNew(cards);
            // 如果this不是甩牌，that是的话就this大
            // that不是的话意味着牌型相同，比较第一张牌即可
            if (!this.isThrow()) {
              if (that.isThrow())
                return true;
              else  // !that.isThrow()
                return this.cards[0].isLargerThan(cards[0]);
            }
            // 下面是this.isThrow()
            // 因为经过了甩牌检测，如果this和that花色相同，则this大
            if (this.cards[0].trumpSuit() == cards[0].trumpSuit()) {
              return true;
            }
            // 下面的隐含条件是不同花色、甩牌
            // 因为不同花色，只要that不是主牌就是this大
            if (!that.cards[0].isTrump()) {
              return true;
            }
            // that是主牌，this肯定不是了。只要牌型相同，that能毙就大
            return !this.samePattern(that);
          }, // canKeepLargestOnCards
          rejectThrowToCards : function(pattern) {
            var rejectedCards = [];
            if (this.cards.length == 0)
              return rejectedCards;
            // 按单牌、对牌、拖拉机的顺序比较this最大的是否比that最小的大，如果是就强制that出最小的
            if (this.singles.length > 0 && pattern.singles.length > 0) {
              var last = pattern.singles[pattern.singles.length - 1];
              if (this.singles[0].weight > last.weight) {
                rejectedCards.push(last);
                return rejectedCards;
              }
            }
            if (pattern.pairs.length > 0) {
              var last = pattern.pairs[pattern.pairs.length - 1];
              if (this.pairs.length > 0) {
                if (this.pairs[0][0].weight > last[0].weight) {
                  rejectedCards = rejectedCards.concat(last);
                  return rejectedCards;
                }
              }
              if (this.tractors.length > 0) {
                for (var i = 0; i < this.tractors.length; i++) {
                  if (this.tractors[i][0].weight > last[0].weight) {
                    rejectedCards = rejectedCards.concat(last);
                    return rejectedCards;
                  }
                }
              }
            } // if (pattern.pairs.length > 0)
            if (this.tractors.length > 0 && pattern.tractors.length > 0) {
              // 拖拉机需要考虑牌数
              for (var i = pattern.tractors.length - 1; i >= 0; i--) {
                for (var j = 0; j < this.tractors.length; j++) {
                  if (this.tractors[j].length < pattern.tractors[i].length)
                    break;
                  if (this.tractors[j][0].weight > pattern.tractors[i][0].weight) {
                    rejectedCards = pattern.tractors[i].concat();
                    return rejectedCards;
                  }
                }
              }
            } // if (this.tractors.length > 0 && pattern.tractors.length > 0)
          } // rejectThrowToCards
        }   // var pattern
        pattern.calculate();
        return pattern;
    } // createNew
  }, // CardPattern
//---------------------------------------------------------------
  Player = {
    INVALID: NaN,
    POS_DOWN: 0,
    POS_RIGHT: 1,
    POS_UP: 2,
    POS_LEFT: 3,
    TEAM_LR: 1,
    TEAM_UD: 0,

    createNew: function(pos) {
      var player = {
        pos: pos,          // 座位
        team: ((pos == Player.POS_LEFT || pos == Player.POS_RIGHT) 
          ? Player.TEAM_LR : Player.TEAM_UD),    // 队伍
        previousPos: (pos + 3) % 4,
        nextPos: (pos + 1) % 4,
        partnerPos: (pos + 2) % 4,

        handyCards: [],    // 手上的牌
        playedCards: [],   // 出过的牌
        
        flipAbilities: [0, 0, 0, 0, 0, 0], // 能否亮某花色的主
        hasSuits: [true, true, true, true, true],  // 是否有某花色的牌，AI使用

        getReadyForNextGame: function() {
          this.handyCards = [];
          this.playedCards = [];

          this.flipAbilities = [0, 0, 0, 0, 0, 0];
          this.hasSuits = [true, true, true, true, true];
        },
        isCollectingMark: function() {
          return game.housePos == this.pos || game.housePos == ((this.pos + 2 ) % 4);
        },
        previousPlayer: function() {
          return game.players[this.previousPos];
        },
        nextPlayer: function() {
          return game.players[this.nextPos];
        },
        partnerPlayer: function() {
          return game.players[this.partnerPos];
        }
      };

      player.checkFlipAbilities = function() {
        this.flipAbilities = [0, 0, 0, 0, 0, 0];
        for (var i = 0; i < this.handyCards.length; i++) {
          var card = this.handyCards[i];
          // 小于副主则停止
          if (card.weight < 12) {
            break;
          }
          this.flipAbilities[card.suit]++;
        }
        // 先处理常主
        for (var i = 0; i < 4; i++) {
          // 没有 或 有且无亮主，则保持原数值
          if (this.flipAbilities[i] == 0 || isNaN(game.flipperPos)) {
            continue;
          }
          // 有一张 且 有亮主，复0
          if (this.flipAbilities[i] == 1 && !isNaN(game.flipperPos)) {
            this.flipAbilities[i] = 0;
            continue;
          }
          // 下面等于this.flipAbilities[i] == 2 && !isNaN(game.flipperPos)
          if (game.flipperPos == this.pos) {
            if (i != game.flippedCards[0].suit) {
              this.flipAbilities[i] = 0;
            }
          } else {  // game.flipperPos == others
            if (game.flippedCards.length == 2) {
              this.flipAbilities[i] = 0;
            }
          }
        }   // for (var i = 0; i < 4; i++)
        // 处理大小王
        if (this.flipAbilities[4] == 1
          || game.flippedSuit() == Card.SUIT_BIG_JOKER 
          || game.flipperPos == this.pos) {
          this.flipAbilities[4] = 0;
        }
        if (this.flipAbilities[5] == 1 || game.flipperPos == this.pos) {
          this.flipAbilities[5] = 0;
        }
      };
      player.checkFlip = function() {
        // if (this.pos == Player.POS_DOWN) {
        if (false) {
          // refresh ui
        } else {
          // 各花色的牌数
          var suitedHandyCards = Card.suitedCards(this.handyCards);
          // 因为每拿一张牌都计算一次，所以不可能有两种花色同时要亮
          // 不需要找出最大牌数，循环到了就可以亮
          for (var i = 0; i < this.flipAbilities.length; i++) {
            if (!this.flipAbilities[i]) {
              continue;
            }
            var progress = this.handyCards.length;
            if (i >= 4) {
              if (progress >= 20 && progress <= 24) {
                if (suitedHandyCards[Card.SUIT_TRUMP].length >= 4) {
                  game.flip(this.pos, i, this.flipAbilities[i]);
                }
              } else if (progress >= 25) {
                if (suitedHandyCards[Card.SUIT_TRUMP].length >= 5) {
                  game.flip(this.pos, i, this.flipAbilities[i]);
                }
              }
            } else {
              if (progress <= 16) {
                if (suitedHandyCards[i].length >= 4) {
                  game.flip(this.pos, i, this.flipAbilities[i]);
                }
              } else if (progress <= 20) {
                if (suitedHandyCards[i].length >= 5) {
                  game.flip(this.pos, i, this.flipAbilities[i]);
                }
              } else if (progress <= 25) {
                if (suitedHandyCards[i].length >= 6) {
                  game.flip(this.pos, i, this.flipAbilities[i]);
                }
              }
            }
          }
        }
      };  // checkFlip
      player.resortCardsAfterFlipping = function() {
        Card.sortCards(this.handyCards);
      };
      player.buryKitty = function() {
        if (this.pos == Player.POS_DOWN) {

        } else {
          // 无用牌数、分数、主牌个数、灭绝多种花色
          var availableScore = 0;
          if (this.handyCards[Card.SUIT_TRUMP].length >= 15) {
            availableScore = 80;
          }
          game.receiveKittyCards(this.handyCards.splice(-8, 8));
        }
      };
      player.canThrow = function(pattern) {
        var suit = pattern.cards[0].trumpSuit(),
          suitedCards = Card.suitedCards(this.handyCards);
          myPattern = CardPattern.createNew(suitedCards[suit]);
          return myPattern.rejectThrowToCards(pattern);
      };
      player.playCard = function() {
        var cards = [];
        if (game.playStep == 0)
          this.playAtFirst(cards);
        else if (game.playStep == 1)
          this.playAtSecond(cards);
        else if (game.playStep == 2)
          this.playAtThird(cards);
        else if (game.playStep == 3)
          this.playAtFourth(cards);
        else
          alert('reach impossible path');
        if (cards.length == 0) alert('play no cards');
        var playedCards = game.tryPlayingCards(this, cards);
        if (playedCards.length == 0) alert('did play no cards');
        this.didPlayCards(playedCards);
      };
      // AI出牌的计算函数----------------------
      

      // AI出牌-------------------------------------
      player.playAtFirst = function(candidates) {
        // 只有一张牌，直接出
        if (this.handyCards.length == 1) {
          candidates.push(this.handyCards[0]);
          return;
        }
        var suitedHandyCards = Card.suitedCards(this.handyCards),
          suitedPlayedCards = Card.suitedCards(Card.playedCards);
        // 出副牌中的最大牌
        {
          for (var suit = 0; suit < 4; suit++) {
            // suit == game.flippedSuit()的数组里肯定为空，所以不用再判断 
            if (suitedHandyCards[suit].length == 0)
              continue;
            // 从已出的牌中查出未出的最大牌
            var weight = 11;
            while (weight >= 0) {
              if (Card.frequency(suitedPlayedCards[suit], Card.getCardBySuitAndWeight(suit, weight)) != 2) {
                break;
              }
              weight--;
            }
            var card = Card.getCardBySuitAndWeight(suit ,weight);
            if (suitedHandyCards[suit].indexOf(card) != -1) {
              candidates.push(card);
              // 对子检测
              if (Card.frequency(this.handyCards, card) == 2) {
                candidates.push(card);
              }
              // 甩牌检测
              while (weight >= 1) {
                weight--;
                card = Card.getCardBySuitAndNumber(suit ,weight);
                var frequency = Card.frequency(this.handyCards, card);
                //TODO：自己没有要看已出牌里是否已经出了
                if (frequency == 0)
                  break;
                while (frequency > 0) {
                  candidates.push(card);
                }
              }
              // 检查敌家是否没有了
              if (!this.previousPlayer().hasSuits[suit] || !this.nextPlayer().hasSuits[suit]) {
                // 如果对家有
                if (this.partnerPlayer().hasSuits[suit]) {
                  candidates = [];
                }
              } else {
                return;
              }
            }
          } // for (var suit = 0; suit < 4; suit++) {
        } // 出副牌中的最大牌
        // 副牌出拖拉机
        {
          for (var suit = 0; suit < 4; suit++) {
            if (suitedHandyCards[suit].length == 0)
              continue;
            var pattern = CardPattern.createNew(suitedHandyCards[suit]);
            if (pattern.tractors.length > 0) {
              var tractor = pattern.tractors[0];
              candidates = tractor.concat();
              return;
            }
          }
          //TODO:甩牌检测
        }
        // 副牌出对子
        {
          for (var suit = 0; suit < 4; suit++) {
            if (suitedHandyCards[suit].length == 0)
              continue;
            var pattern = CardPattern.createNew(suitedHandyCards[suit]);
            if (pattern.pairs.length != 0) {
              // TODO:确保这是大的
              var weight = pattern.pairs[0][0].weight;
              candidates = pattern.pairs[0].concat();
              //TODO:不大的话，加到pending里，
            }
          }
        }
        // 主牌出拖拉机
        {
          var pattern = CardPattern.createNew(suitedHandyCards[Card.SUIT_TRUMP]);
          if (pattern.tractors.length > 0) {
            // 带王的不出
            if (!pattern.tractor[0][0].isJoker()) {
              candidates = pattern.tractors[0].concat();
              return;
            }
            // 出不带王的第2个拖拉机
            if (pattern.tractors.length >= 2) {
              candidates = pattern.tractors[1].concat();
              return;
            }
          }
        }
        // 副牌出单牌
        {
          for (var suit = 0; suit < 4; suit++) {
            if (suitedHandyCards[suit].length == 0)
              continue;

          }
        }
        if (candidates.length == 0) {
          candidates.push(this.handyCards[0]);
        }
      };  // playAtFirst
      player.playAtSecond = function(candidates) {
        // 如果跟手牌数一样，直接全部出
        if (game.currentRoundCards[game.firstPlayPos].length == this.handyCards.length) {
          for (var i = 0; i < this.handyCards.length; i++) {
            candidates.push(this.handyCards[i]);
            return;
          }
        }

        var suit = game.currentRoundCards[game.firstPlayPos][0].suit,
          suitedHandyCards = Card.suitedCards(this.handyCards);
        // 没有这种花色
        if (suitedHandyCards[suit].length == 0) {

          return;
        }

        var remain = game.largestPattern.cards.length - suitedHandyCards[suit];
        // 该花色，手牌数=要出的数，直接全部出
        if (remain == 0) {
          for (var i = 0; i < suitedHandyCards.length; i++) {
            candidates.push(suitedHandyCards[0]);
          }
          return;
        }
        // 如果该花色要出的牌数大于手牌数
        if (remain > 0) {
          // 先把手牌出完
          for (var i = 0; i < suitedHandyCards.length; i++) {
            candidates.push(suitedHandyCards[0]);
          }
          // 如果对家有，那就是大不了，要垫牌
          if (this.partnerPlayer.hasSuits[suit]) {

          } else {

          }
          return;
        }
        // remain < 0，需要选牌来出
        
      };  // playAtSecond
      player.playAtThird =  function(candidates) {
        if (candidates.length == 0) {
          candidates.push(this.handyCards[0]);
        }
      };
      player.playAtFourth = function(candidates) {
        if (candidates.length == 0) {
          candidates.push(this.handyCards[0]);
        }
      };
      //
      player.didPlayCards = function(cards) {
        // 从手牌中移除
        for (var i = 0; i < cards.length; i++) {
          var index = this.handyCards.indexOf(cards[i]);
          if (index == -1) alert('index == -1');
          this.handyCards.splice(index, 1);
        }
      };
      player.drawOneCard = function(card) {
        // if (card.weight >= 12) {
        //  this.handyCards[Card.SUIT_TRUMP].push(card);
        // }
        this.handyCards.push(card);
        Card.sortCards(this.handyCards);
        this.checkFlipAbilities();
        this.checkFlip();
      };
      player.drawKitty = function(cards) {
        this.handyCards = this.handyCards.concat(cards);
        Card.sortCards(this.handyCards);
      }
      return player;
    },  // createNew
  },  // Player
//---------------------------------------------------------------------
  game = (function() {
    var game = {
      currentRoundCards: [[], [], [], []],
      lastRoundCards: [[], [], [], []],
      kittyCards: [],
      flippedCards: [],
      flipperPos: Player.INVALID,
      masters: [Card.NUMBER_2, Card.NUMBER_2],
      housePos: Player.INVALID,
      score: 0,
      firstGame: true,
      players: [Player.createNew(Player.POS_DOWN), Player.createNew(Player.POS_RIGHT),
        Player.createNew(Player.POS_UP), Player.createNew(Player.POS_LEFT)],
      // 发牌用
      dealingCards: undefined,
      firstDealPos: NaN,
      dealTimer: null,
      // 出牌用
      currentLargestPos: NaN,
      largestPattern: NaN,
      firstPlayPos: NaN,
      playStep: NaN,

      flip: function(pos, suit, count) {
        if (this.firstGame) {
          this.housePos = pos;
        }
        this.flipperPos = pos;
        this.flippedCards = [];
        Card.calculateOrders(suit, this.masterNow());
        this.currentRoundCards = [[], [], [], []];
        
        if (!isNaN(suit)) {
          var card = Card.getCardBySuitAndNumber(suit, this.masterNow());
          for (var i = 0; i < count; i++) {
            this.flippedCards[i] = card;
          }
          for (var i = 0; i < this.players.length; i++) {
            this.players[i].resortCardsAfterFlipping();
          }
          this.currentRoundCards[pos] = this.flippedCards;
        }
      },
      flippedSuit: function() {
        if (this.flippedCards.length > 0) {
          return this.flippedCards[0].suit;
        } else {
          return Card.INVALID;
        }
      },
      houseTeam: function() {
        return isNaN(this.housePos) ? Player.INVALID : this.players[this.housePos].team;
      },
      masterNow: function() {
        return isNaN(this.housePos) ? Card.NUMBER_2 : this.masters[this.houseTeam()];
      },
      
    };
    
    // 发牌---------------------------
    game.beginDealing = function() {
      this.flip(Player.INVALID, Card.INVALID, 0);
      this.score = 0;
      this.currentRoundCards = [[], [], [], []];
      this.lastRoundCards = [[], [], [], []];

      this.dealingCards = Card.getShuffledCardsForDealing();
      this.firstDealPos = this.firstGame ? (Math.floor(Math.random())) % 4 : this.housePos;
      
      this.kittyCards = [];
      for (var i = 0; i < 8; i++) {
        this.kittyCards.push(this.dealingCards.pop());
      }
      
      this.continueDealing();
    };
    game.dealCard = function() {
      if (this.dealingCards.length == 0) {
        this.finishDealing();
        return;
      }

      this.continueDealing();
      var next = (this.firstDealPos + 100 - this.dealingCards.length) % 4;
      this.players[next].drawOneCard(this.dealingCards.pop());
    };
    game.continueDealing = function() {
      this.dealTimer = setTimeout("game.dealCard();", 1);  // 发牌间隔
    }
    game.pauseDealing = function() {
      clearTimeout(this.dealTimer);
      this.dealTimer = null;
    };
    game.finishDealing = function() {
      this.pauseDealing();
      // 如果第一局没人亮主来决定庄家，则重新发牌
      if (this.firstGame && isNaN(this.housePos)) {
        // TODO：重新发牌
      } else {
        this.firstGame = false;
        setTimeout("game.dealKitty();", 10);
      }
    };
    // 扣底----------------------------------------
    game.dealKitty = function() {
      this.currentRoundCards = [[], [], [], []];
      this.players[this.housePos].drawKitty(this.kittyCards);
      this.kittyCards = [];
      game.bury();
    }
    game.bury = function() {
      this.players[this.housePos].buryKitty();
    };
    game.receiveKittyCards = function(cards) {
      this.kittyCards = cards;
      this.beginPlaying();
    };
    // 出牌-----------------------------------------
    game.beginPlaying = function() {
      this.currentLargestPos = this.housePos;
      this.largestPattern = NaN;
      this.nextPlayRound();
    };
    game.nextPlayRound = function() {
      this.firstPlayPos = this.currentLargestPos;
      this.playStep = 0;
      this.currentRoundCards = [[], [], [], []];
      this.nextPlayStep();
    };
    game.nextPlayStep = function() {
      if (this.playStep == 4) {
        //
        this.lastRoundCards = this.currentRoundCards;
        // 算分
        if (this.players[this.housePos].isCollectingMark()) {
          this.score += this.getMarksInCurrentRound();
        }
        var shuoldFinishGame = (this.players[0].handyCards.length == 0);
        if (shuoldFinishGame) {
          // 底牌分
          var kittyMarks = Card.getMarks(this.kittyCards),
            times = 2;
          if (this.largestPattern.tractors.largestPattern > 0) {
            times = Math.pow(2, this.largestPattern.tractors[0].length);
          } else if (this.largestPattern.pairs.length > 0) {
            times = 4;
          }
          this.score += kittyMarks * times;

          setTimeout("game.finishGame()", 10);
        } else {
          setTimeout("game.nextPlayRound()", 1000);
        }
      } else {
        setTimeout("game.notifyPlayCard()", 1);
      }
    };
    game.notifyPlayCard = function() {
      var pos = (this.firstPlayPos + this.playStep) % 4;
      this.players[pos].playCard();
    };
    game.tryPlayingCards = function(player, cards) {
      // 拒绝甩牌的检测
      if (this.playStep == 0) {
        var pattern = CardPattern.createNew(cards);
        if (pattern.isThrow()) {
          // 被拒的牌中最小的，实际就是牌数最少的
          var smallest = [];
          for (var i = 1; i < 4; i++) {
            // 按出牌顺序逐个询问
            var pos = (this.firstPlayPos + i) % 4,
              rejectedCards = this.players[pos].canThrow();
            if (rejectedCards.length != 0) {
              if (smallest.length == 0 || smallest.length > rejectedCards.length) {
                smallest = rejectedCards;
              }
              // 如果被拒的是单牌，不用再继续询问下个player了
              if (smallest.length == 1) {
                break;
              }
            }
          } // for (var i = 1; i < 4; i++)
          // 如果被拒的牌不为0，返回
          if (smallest.length > 0) {
            cards = smallest;
          }
        }
      }

      this.currentRoundCards[player.pos] = cards;
      // 判断目前最大的牌
      if (this.playStep == 0) {
        this.largestPattern = CardPattern.createNew(cards);
      } else {
        if (this.largestPattern.canKeepLargestOnCards(cards)) {
          this.largestPattern = CardPattern.createNew(cards);
        }
        // AI:判断是否没有某种花色了
        var suit = cards[0].trumpSuit();
        if (player.hasSuits[suit]) {
          if (Card.sameSuit(cards)) {
            player.hasSuits[suit] = suit == this.currentRoundCards[this.firstPlayPos][0].trumpSuit();
          } else {
            player.hasSuits[suit] = false;
          }
        }
      }

      Card.playedCards = Card.playedCards.concat(cards);
      Card.sortCards(Card.playedCards);

      this.playStep++;
      this.nextPlayStep();

      return cards;
    };
    game.finishGame = function() {
      // 计算下一局的主
      var grade = 3;  // 0分的时候升3级
      if (this.score > 0) {
        grade = 2 - Math.floor(this.score / 40);
      }
      // 如果不大于0,表示下台。是负数，则表示另一team升级
      // pos % 2 = team
      var victorTeam = grade > 0 ? (this.housePos % 2) : ((this.housePos + 1) % 2);
      this.masters[victorTeam] = (this.masters[victorTeam] + Math.abs(grade)) % 13;
      this.housePos = (this.housePos + (grade > 0 ? 2 : 1)) % 4;

      Card.playedCards = [];

      alert('shit!');
    };
    game.getMarksInCurrentRound = function() {
      var mark = 0;
      for (var i = 0; i < this.currentRoundCards.length; i++) {
        mark += Card.getMarks(this.currentRoundCards[i]);
      }
      if (mark % 5 != 0) alert('mark % 5 != 0');
      return mark;
    };
    game.onclick = function(e) {
      game.beginDealing();
    };
    return game;
  })(),
//----------------------------------------------------------------------
  ui = {
    showFlipPanel: false,

  };

  var positionTexts = ["下", "右", "上", "左", "无"];
  function drawFrame(ctx) {
    ctx.drawImage(assets.desktop, 0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.font = "bold 15px Arial";
    ctx.textBaseline = "top";
    ctx.fillStyle = 'red';
    //
    var text = '现在打：';
    if (game.firstGame)
      text += "2 VS 2"
    else
      text += assets.numberTexts[game.masters[game.houseTeam()]]
         + ' VS '
         + assets.numberTexts[game.masters[(game.houseTeam() + 1) % 2]];
    ctx.fillText(text, 0, 0);
    //
    text = '亮主：' + positionTexts[isNaN(game.flipperPos) ? 4 : game.flipperPos];
    ctx.fillText(text, 0, 30);
    var measuredWidth = ctx.measureText(text).width;
    var index = [1, 14, 27, 40, 52, 53];
    for (var i = 0; i < game.flippedCards.length; i++) {
      ctx.drawImage(assets.cardImages[index[game.flippedCards[i].suit]], 25, 38, 20, 20, measuredWidth + 5 + i * 25, 30, 20, 20);
    }
    //
    text = '庄家：' + positionTexts[isNaN(game.housePos) ? 4 : game.housePos];
    measuredWidth = ctx.measureText(text).width + 20;
    ctx.fillText(text, ctx.canvas.width - measuredWidth, 0);
    //
    text = '分数：' + game.score;
    ctx.fillText(text, ctx.canvas.width - measuredWidth, 30);

    // text = game.players[0].debug;
    // ctx.fillText(text, 30, ctx.canvas.height - 70);

    // text = game.players[1].debug;
    // ctx.fillText(text, 50, ctx.canvas.height - 90);

    // text = game.players[2].debug;
    // ctx.fillText(text, 30, ctx.canvas.height - 110);

    // text = game.players[3].debug;
    // ctx.fillText(text, 10, ctx.canvas.height - 130);

    // text = game.kittyDebug;
    // ctx.fillText(text, 30, ctx.canvas.height - 150);

    // draw handy cards-----------------------------
    for (var i = 0; i < game.players[Player.POS_DOWN].handyCards.length; i++) {
      ctx.drawImage(assets.cardImages[game.players[0].handyCards[i].index], 150 + 12 * i, 600 - 96);
    }
    ctx.save();
    ctx.rotate(-90 * Math.PI / 180);
    for (var i = 0; i < game.players[Player.POS_RIGHT].handyCards.length; i++) {
      ctx.drawImage(assets.cardImages[game.players[1].handyCards[i].index], -550 + 12 * i, 800 - 96);
    }
    ctx.restore();
    for (var i = 0; i < game.players[2].handyCards.length; i++) {
      ctx.drawImage(assets.cardImages[game.players[2].handyCards[i].index], 150 + 12 * i, 0);
    }
    ctx.save();
    ctx.rotate(90 * Math.PI / 180);
    for (var i = 0; i < game.players[3].handyCards.length; i++) {
      ctx.drawImage(assets.cardImages[game.players[3].handyCards[i].index], 100 + 12 * i, -96);
    }
    ctx.restore();
    // draw playing cards ---------------------------------------
    for (var i = 0; i < game.currentRoundCards[Player.POS_DOWN].length; i++) {
        ctx.drawImage(assets.cardImages[game.currentRoundCards[Player.POS_DOWN][i].index], 300 + 12 * i, 600 - 96 - 96);
    }
    for (var i = 0; i < game.currentRoundCards[Player.POS_RIGHT].length; i++) {
        ctx.drawImage(assets.cardImages[game.currentRoundCards[Player.POS_RIGHT][i].index], 600 + 12 * i, 300 - 96 / 2);
    }
    for (var i = 0; i < game.currentRoundCards[Player.POS_UP].length; i++) {
      ctx.drawImage(assets.cardImages[game.currentRoundCards[Player.POS_UP][i].index], 300 + 12 * i, 96);
    }
    for (var i = 0; i < game.currentRoundCards[Player.POS_LEFT].length; i++) {
      ctx.drawImage(assets.cardImages[game.currentRoundCards[Player.POS_LEFT][i].index], 100 + 12 * i, 300 - 96 / 2);
    }
    // draw kitty cards -----------------------------------------
    var y = (600 - 96) / 2, x = 800 / 2 - 12 * 4;
    for (var i = 0; i < game.kittyCards.length; i++) {
      ctx.drawImage(assets.cardImages[game.kittyCards[i].index], x + 12 * i, y);
    }

    if (ui.showFlipPanel) {

    }
  };

  function loaded() {
    var canvas = document.getElementById('mycanvas'),
      context = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 600;
    canvas.onclick = function(e) {
      game.onclick(e);
    };
    setInterval(function() {
      drawFrame(context);
    }, 1);
  };

  unitTests = {
    // 根据文字获取牌
    getCardByText: function(text) {
      var number = assets.numberTexts.indexOf(text.substring(2, 3)),
        suit = assets.suitTexts.indexOf(text.substring(0, 2));
      return Card.getCardBySuitAndNumber(suit, number);
    },
    getCardsByTexts: function(texts) {
      var cards = [];
      for (var i = 0; i < texts.length; i++) {
        cards[i] = this.getCardByText(texts[i]);
      }
      return cards;
    },
    testCardPattern: function() {
      var cards = this.getCardsByTexts(['黑桃A', '黑桃K', '黑桃K']);
    }
  };

</script>
</head>

<body onload="loaded()">
<canvas id="mycanvas">你的浏览器不支持</canvas>
</body>
</html>